package com.gwl.concurrent.notify;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.lang.System.out; 

public class WaxOMatic2 {
	static class Car { 
		private Lock lock = new ReentrantLock(); 
		private Condition condition = lock.newCondition(); 
		private volatile boolean waxOn = false; 
		public void waxed() throws InterruptedException { 
			lock.lock(); 
			try { 
				waxOn = true; 
				TimeUnit.MILLISECONDS.sleep(200); 
				condition.signalAll(); 
			} finally { 
				lock.unlock(); 
			}
		} 
		public void buff() throws InterruptedException { 
			lock.lock(); 
			try { 
				waxOn = false; 
				TimeUnit.MILLISECONDS.sleep(200); 
				condition.signalAll(); 
			} finally { 
				lock.unlock(); 
			}
		} 
		public void waitForWaxing() throws InterruptedException { 
			lock.lock(); 
			try { 
				while(waxOn == false) { 
					condition.await(); 
				}
			} finally { 
				lock.unlock(); 
			}
		}
		public void waitForBuffing() throws InterruptedException { 
			lock.lock(); 
			try { 
				while(waxOn == true) { 
					condition.await(); 
				}
			} finally { 
				lock.unlock(); 
			}
		}
	}
	
	static class WaxOn implements Runnable { 
		private Car car; 
		WaxOn(Car c) { 
			car = c; 
		} 
		@Override
		public void run() {
			try { 
				while(!Thread.interrupted()) { 
					car.waitForBuffing();
					out.println("Wax On! "); 
					car.waxed(); 
				}
			} catch (InterruptedException e) {
				out.println("WaxOn InterruptedException! "); 
			} 
			out.println("Wax Exit On! "); 
		} 
	}
	static class WaxOff implements Runnable { 
		private Car car; 
		WaxOff(Car c) { 
			car = c; 
		} 
		@Override
		public void run() {
			try { 
				while(!Thread.interrupted()) { 
					car.waitForWaxing(); 
					out.println("Wax Off! "); 
					car.buff(); 
				}
			} catch (InterruptedException e) {
				out.println("WaxOn InterruptedException! "); 
			} 
			out.println("Wax Off Exit! "); 
		} 
	}
	
	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		Car car = new Car(); 
		ExecutorService exec = Executors.newCachedThreadPool(); 
		exec.execute(new WaxOff(car)); 
		exec.execute(new WaxOn(car)); 
		TimeUnit.SECONDS.sleep(3); 
		exec.shutdownNow(); 
	}

}
